<?php
namespace App\Models;

use Illuminate\Http\Request;


/**
 * 日志类
 */
class MConfig extends BaseModel
{
    public $table = "configs";

    const TITLE_MODE = "config_mode_setting";
    const TITLE_RECEIPT = "config_receipt_setting";
    const TITLE_VIRTUAL = "config_virtual_setting";
    const TITLE_COIN = "config_coin_setting";
    const TITLE_WEB = "config_web_setting";
    const TITLE_THEME = "login-theme";
    const TITLE_SPREE_BOX = "config_vip_spree_box";

    const TITLE_VIP = "vip";   //VIP等级的配置项标题标识

    private static $instances = array(); //单例模式项实例对象数组
    public $kvList = array();  //单个实例的配置项参数列表

    /**
     * （单例模式）获取结算规则的配置项实例
     * @param string $title 配置项标题
     * @return MConfig
     */
    public static function getInstance($title)
    {
        //如果还没有缓存此配置项，或者缓存的配置项为NULL，就重新去数据库读取配置项
        if (!array_has(self::$instances, $title) || empty(self::$instances[$title])) {
            $ins = MConfig::where("title", $title)->first();
            if (!empty($ins)) {
                $ins = $ins->parseWork();     //解析配置参数（返回解析后的对象）
            }
            self::$instances[$title] = $ins;
        }
        return self::$instances[$title];
    }

    /**
     * 更新一个实例模型
     * @param MConfig $ins 新的实例对象，会立即替换掉当前缓存的对象实例
     */
    public static function setInstance(MConfig $ins)
    {
        //只有实例不为NULL的时候并且实例拥有title属性时，才会去替换配置对象
        if (!empty($ins) && !empty($ins->title)) {
            $ins = $ins->parseWork();     //解析配置参数
            self::$instances[$ins->title] = $ins;
        }
    }

    /**
     * 实例模型初始化构建，解析出数据库中序列化的键值对属性
     *
     * @return MConfig 返回当前的实例对象
     */
    public function parseWork()
    {
        if (!empty($this->kv)) {
            //只是单纯的把配置项解析到缓存当中，如有特殊用途的配置项，请参考VIP会员等级对象进行封装用法
            $this->kvList = unserialize($this->kv);
        }
        return $this;
    }

    /**
     * 获取指定配置项的配置参数值（简单取值）（title.key）
     *
     * @param string $key title.key(标题.配置项键名)
     *
     * @param mixed $default 默认值（当未能取得结果时，返回此值, 未指定此值默认为 "" ）
     *
     * @return mixed 根据存储时设定的规则，返回数据
     */
    public static function getAttr($key, $default = "")
    {
        $key = explode(".", $key);

        if (empty($key) || count($key) <2) {
            return $default;
        }

        $title = $key[0];  //得到标题
        $key = $key[1];     //得到配置项键值

        $config = MConfig::getInstance($title);

        if (empty($config)) {
            return $default;    //如果未能成功取出配置项的值，直接返回默认值
        }

        //简单取值，直接取出对应配置项的值，复杂的取值操作请使用 getObject()
        if (isset($config->kvList[$key])) {
            $default = $config->kvList[$key];
        }

        return $default;
    }

    /**
     * 以对象访问方式，循环获取配置参数值（深度取值）（title.key.key...）
     *
     * @param string $key title.key(标题.配置项键名)
     *
     * @param mixed $default 默认值（当未能取得结果时，返回此值, 未指定此值默认为 "" ）
     *
     * @return mixed
     */
    public static function getObject($key, $default = "")
    {
        $key = explode(".", $key);

        //最少应保证有两个键值（标题和配置项）
        if (empty($key) || !isset($key[0], $key[1])) {
            return $default;
        }

        $config = MConfig::getInstance($key[0]);
        if (empty($config)) {
            return $default;    //如果未能成功取出配置项的值，直接返回默认值
        }

        $depth = null;  //标识当前深度的对象/数组
        $level = 1;     //标识当前深度的等级
        do {
            //首先判断是否存储的有此配置项
            if (!isset($config->kvList[$key[$level]])) {
                break;
            }

            $depth = $config->kvList[$key[$level]];

            //未能找到指定的对象，就直接退出循环
            if (empty($depth)) {
                break;
            }

            //判断是否达到了最后一层，如果达到最后一层，则代表值已经取出
            if ($level == count($key) - 1) {
                $default = $depth;
            }

            $level++;
        } while (!empty($depth) && $level < count($key));

        return $default;
    }

    /**
     * 存储配置项数据到数据库
     * @param string $title 配置项标题
     * @param array $kv 配置项参数数组
     * @return bool
     */
    public static function saveAttr($title, $kv)
    {
        $config = MConfig::getInstance($title);
        if (empty($config)) {
            $config = new MConfig();
            $config->title = $title;
        }

        $config->kv = serialize($kv);
        $config->time = time();
        return $config->save();
    }

    /**
     * 保存当前所持有的配置项
     */
    public function saveSelf()
    {
        $this->kv = serialize($this->kvList);
        $this->time = time();
        return $this->save();
    }

    /**
     * 获取指定配置的全部键名
     * @param string $title 配置项标题
     * @return mixed
     */
    public static function getKeys($title)
    {
        $config = MConfig::getInstance($title);
        return array_keys($config->kvList);
    }

    /**
     * 站点信息存储验证
     * $flag 0 = rules  1 = msg
     */
    public static function webRulesWithMsg($flag)
    {
        $rules = array(
            "kv_site_name"  => "required|max:100",
            'kv_site_open'  => 'required|numeric',
            'kv_site_intro' => 'required',
        );

        $msg = array(
            "kv_site_name.required"  => "请填写站点名称！",
            "kv_site_name.max"       => "站点名称最长为100个字符！",
            "kv_site_open.required"  => "站点状态必须选择！",
            "kv_site_intro.required" => "站点描述必须填写！"
        );

        if ($flag == 0) {
            return $rules;
        } else {
            return $msg;
        }
    }

    /**
     * 一键初始化密码验证
     */
    public static function initialValid(Request $request)
    {
        $admin = Admin::where('username', ss("admin_name"))->where('userpass', md5($request->userpass))->first();
        if (empty($admin)) {
            return null;
        }
        return $admin;
    }
}